
#include "drawable.h"
#include <GL/gl.h>
#include <GL/glu.h>

// Sphere =================================================================================

/// A Sphere object.
Sphere sphere;

/// Constructor of Sphere
Sphere::Sphere (float r) {
    radius = r;
    box = Bbox (-1,-1,-1,1,1,1);
}

/// Draw method for spheres. 
void Sphere::draw () const
{
    static GLUquadric* quadric = 0;
    if (quadric == 0) { 
       quadric = gluNewQuadric();
       //~ gluQuadricOrientation(quadric, GLU_OUTSIDE);
    }
    gluSphere(quadric, radius, 10, 10);
   
    //~ glBegin(GL_POINTS);
    //~ glVertex3f (0,0,0);
    //~ glEnd ();
}

// Dot ====================================================================================

/// A Dot object
Dot dot;

/// Constructor of Dot
/// @param wid width of the dot in pixels.
Dot::Dot (float wid) : width(wid) {
    box = Bbox (0,0,0,0,0,0);
}

/// Draw method for dots. 
void Dot::draw () const
{
    //~ glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
    //~ glEnable (GL_POINT_SMOOTH);
    //~ glEnable(GL_BLEND);
    //~ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   
    //~ glPointSize (width);
    glBegin(GL_POINTS);
    glVertex3f (0,0,0);
    glEnd ();
    
    //~ glDisable(GL_BLEND);
}

// DrawableInstance =======================================================================

/// Constructor of DrawableInstance
DrawableInstance::DrawableInstance (Drawable* d, const Transformation& t)
    : obj (d)
{ 
    setTransformation(t);
}

/// Constructor of DrawableInstance
DrawableInstance::DrawableInstance (DrawableInstance& di) 
{
    obj = di.obj;
    setTransformation (di.transform);
}

/// Sets the transformation
void DrawableInstance::setTransformation(const Transformation&t)
{
    transform = t;
    if (obj) {
      Point min = t.transform(Point (obj->box.xmin(), obj->box.ymin(), obj->box.zmin()));
      Point max = t.transform(Point (obj->box.xmax(), obj->box.ymax(), obj->box.zmax()));
      box = Bbox (min.x(),min.y(),min.z(),max.x(),max.y(),max.z());
    }
}

/// Draws the instance.
/// Assumes the current matrix is MODELVIEW.
void DrawableInstance::draw () const
{
    glPushMatrix ();
    glTransform (transform);
    obj->draw ();
    glPopMatrix ();
}

// DrawableInstanceGroup =================================================================

/// Constructor
DrawableInstanceGroup::DrawableInstanceGroup () : DrawableInstance (0, Transformation(CGAL::IDENTITY)) 
{
}

/// Adds another instance to the group
/// @param instance instance to be added
bool DrawableInstanceGroup::add (DrawableInstance * instance) 
{
    for (int i = 0; i < group.size (); i++) {
      if (instance == group [i]) return false;
    }
    group.push_back (instance);
    setTransformation (transform);
    return true;
}

/// Propagates the group transformation to each of its children
void DrawableInstanceGroup::propagateTransformation () 
{
    for (int i = 0; i < group.size(); i++) {
      DrawableInstance* obj = group [i];
      obj->setTransformation (transform * obj->getTransformation ());
    }
    setTransformation (Transformation(CGAL::IDENTITY));
}

/// Infers a translation transformation for the group from the average position of its
/// children. The transformations of the children objects are also modified
/// to take this into account. 
void DrawableInstanceGroup::inferGroupTransformation () 
{
    // we assume that the center of the group is the center of the bounding box.
    Point center = box.center();
    Transformation s (CGAL::TRANSLATION, Point(0,0,0) - center);
    for (int i = 0; i < group.size(); i++) {
      DrawableInstance* obj = group [i];
      obj->setTransformation (s * obj->getTransformation ());
    }
    setTransformation (Transformation (CGAL::TRANSLATION, center - Point(0,0,0)));
}

/// Sets the transformation
void DrawableInstanceGroup::setTransformation(const Transformation&t)
{
    transform = t;
    for (int i = 0; i < group.size(); i++) {
      DrawableInstance* obj = group [i];
      Point min = t.transform(Point (obj->box.xmin(), obj->box.ymin(), obj->box.zmin()));
      Point max = t.transform(Point (obj->box.xmax(), obj->box.ymax(), obj->box.zmax()));
      Bbox b (min.x(),min.y(),min.z(),max.x(),max.y(),max.z());
      if (i == 0) box = b;
      else box = box + b;
    }
}

/// Draws the instance group.
/// Assumes the current matrix is MODELVIEW.
void DrawableInstanceGroup::draw () const
{
    glPushMatrix ();
    glTransform (transform);
    for (int i = 0; i < group.size(); i++) {
      group[i]->draw ();
    }
    glPopMatrix ();
}

// Other stuff ===========================================================================

/// Performs a glMultMatrix where the matrix is a CGAL transformation.
/// @param t CGAL tranformation matrix.
void glTransform (const Transformation &t)
{
    double matrix [] = { t.m(0,0), t.m(1,0), t.m(2,0), 0,
                         t.m(0,1), t.m(1,1), t.m(2,1), 0,
                         t.m(0,2), t.m(1,2), t.m(2,2), 0,
                         t.m(0,3), t.m(1,3), t.m(2,3), 1 };
    glMultMatrixd (matrix);
}

